Skip to content

Conversation

jtstogel
Copy link
Contributor

@jtstogel jtstogel commented Oct 7, 2025

This patch provides definitions for pkey_* functions for linux x86_64.

alloc, free, and mprotect are simple syscall wrappers. pkey_set and pkey_get modify architecture-specific registers. The logic for these live in architecture specific directories:

  • libc/src/sys/mman/linux/x86_64/pkey_common.h has a real implementation
  • libc/src/sys/mman/linux/generic/pkey_common.h contains stubs that just return ENOSYS.

This is my first addition to LLVM libc. It's likely I've missed something that needs updating -- ample feedback is welcome!

@llvmbot llvmbot added libc bazel "Peripheral" support tier build system: utils/bazel labels Oct 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 7, 2025

@llvm/pr-subscribers-libc

Author: Jackson Stogel (jtstogel)

Changes

This patch provides definitions for pkey_* functions for linux x86_64.

alloc, free, and mprotect are simple syscall wrappers. pkey_set and pkey_get modify architecture-specific registers. The logic for these live in architecture specific directories:

  • libc/src/sys/mman/linux/x86_64/pkey_common.h has a real implementation
  • libc/src/sys/mman/linux/generic/pkey_common.h contains stubs that just return ENOSYS.

This is my first addition to LLVM libc. It's likely I've missed something that needs updating -- ample feedback is welcome!


Patch is 30.82 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/162362.diff

22 Files Affected:

  • (modified) libc/config/linux/x86_64/entrypoints.txt (+5)
  • (modified) libc/src/sys/mman/CMakeLists.txt (+35)
  • (modified) libc/src/sys/mman/linux/CMakeLists.txt (+75)
  • (added) libc/src/sys/mman/linux/generic/CMakeLists.txt (+9)
  • (added) libc/src/sys/mman/linux/generic/pkey_common.h (+25)
  • (added) libc/src/sys/mman/linux/pkey_alloc.cpp (+37)
  • (added) libc/src/sys/mman/linux/pkey_free.cpp (+35)
  • (added) libc/src/sys/mman/linux/pkey_get.cpp (+35)
  • (added) libc/src/sys/mman/linux/pkey_mprotect.cpp (+45)
  • (added) libc/src/sys/mman/linux/pkey_set.cpp (+35)
  • (added) libc/src/sys/mman/linux/x86_64/CMakeLists.txt (+10)
  • (added) libc/src/sys/mman/linux/x86_64/pkey_common.h (+71)
  • (added) libc/src/sys/mman/pkey_alloc.h (+20)
  • (added) libc/src/sys/mman/pkey_free.h (+20)
  • (added) libc/src/sys/mman/pkey_get.h (+20)
  • (added) libc/src/sys/mman/pkey_mprotect.h (+21)
  • (added) libc/src/sys/mman/pkey_set.h (+20)
  • (modified) libc/test/src/sys/mman/linux/CMakeLists.txt (+21)
  • (added) libc/test/src/sys/mman/linux/pkey_test.cpp (+241)
  • (modified) utils/bazel/llvm-project-overlay/libc/BUILD.bazel (+75)
  • (modified) utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel (+1)
  • (modified) utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel (+18)
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 87b78a337b875..4c56d23d96877 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -264,6 +264,11 @@ set(TARGET_LIBC_ENTRYPOINTS
     libc.src.sys.mman.munlock
     libc.src.sys.mman.munlockall
     libc.src.sys.mman.munmap
+    libc.src.sys.mman.pkey_alloc
+    libc.src.sys.mman.pkey_free
+    libc.src.sys.mman.pkey_get
+    libc.src.sys.mman.pkey_mprotect
+    libc.src.sys.mman.pkey_set
     libc.src.sys.mman.remap_file_pages
     libc.src.sys.mman.posix_madvise
     libc.src.sys.mman.shm_open
diff --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt
index 4d4c2ad376050..c7be1eddacb5e 100644
--- a/libc/src/sys/mman/CMakeLists.txt
+++ b/libc/src/sys/mman/CMakeLists.txt
@@ -86,6 +86,41 @@ add_entrypoint_object(
     .${LIBC_TARGET_OS}.msync
 )
 
+add_entrypoint_object(
+  pkey_alloc
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.pkey_alloc
+)
+
+add_entrypoint_object(
+  pkey_free
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.pkey_free
+)
+
+add_entrypoint_object(
+  pkey_get
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.pkey_get
+)
+
+add_entrypoint_object(
+  pkey_mprotect
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.pkey_mprotect
+)
+
+add_entrypoint_object(
+  pkey_set
+  ALIAS
+  DEPENDS
+    .${LIBC_TARGET_OS}.pkey_set
+)
+
 add_entrypoint_object(
   remap_file_pages
   ALIAS
diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 7181bb98a187f..1c79180cbcabb 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -1,3 +1,10 @@
+add_subdirectory(generic)
+set(ARCH_SUBDIRECTORY generic)
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+  add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
+  set(ARCH_SUBDIRECTORY ${LIBC_TARGET_ARCHITECTURE})
+endif()
+
 add_entrypoint_object(
   madvise
   SRCS
@@ -166,6 +173,74 @@ add_entrypoint_object(
     libc.src.errno.errno
 )
 
+add_entrypoint_object(
+  pkey_alloc
+  SRCS
+    pkey_alloc.cpp
+  HDRS
+    ../pkey_alloc.h
+  DEPENDS
+    libc.include.sys_mman
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  pkey_free
+  SRCS
+    pkey_free.cpp
+  HDRS
+    ../pkey_free.h
+  DEPENDS
+    libc.include.sys_mman
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  pkey_get
+  SRCS
+    pkey_get.cpp
+  HDRS
+    ../pkey_get.h
+  DEPENDS
+    libc.include.sys_mman
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+    .${ARCH_SUBDIRECTORY}.pkey_common
+)
+
+add_entrypoint_object(
+  pkey_mprotect
+  SRCS
+    pkey_mprotect.cpp
+  HDRS
+    ../pkey_mprotect.h
+  DEPENDS
+    libc.include.sys_mman
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.sys.mman.mprotect
+    libc.src.errno.errno
+)
+
+add_entrypoint_object(
+  pkey_set
+  SRCS
+    pkey_set.cpp
+  HDRS
+    ../pkey_set.h
+  DEPENDS
+    libc.include.sys_mman
+    libc.include.sys_syscall
+    libc.src.__support.OSUtil.osutil
+    libc.src.errno.errno
+    .${ARCH_SUBDIRECTORY}.pkey_common
+)
+
 add_entrypoint_object(
   remap_file_pages
   SRCS
diff --git a/libc/src/sys/mman/linux/generic/CMakeLists.txt b/libc/src/sys/mman/linux/generic/CMakeLists.txt
new file mode 100644
index 0000000000000..42b6d96c8387e
--- /dev/null
+++ b/libc/src/sys/mman/linux/generic/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_header_library(
+  pkey_common
+  HDRS
+    pkey_common.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.src.__support.common
+    libc.src.__support.error_or
+)
diff --git a/libc/src/sys/mman/linux/generic/pkey_common.h b/libc/src/sys/mman/linux/generic/pkey_common.h
new file mode 100644
index 0000000000000..0811cfb77d4b0
--- /dev/null
+++ b/libc/src/sys/mman/linux/generic/pkey_common.h
@@ -0,0 +1,25 @@
+#ifndef LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
+#define LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace pkey_common {
+
+LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
+  (void)pkey;
+  return Error(ENOSYS);
+}
+
+LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
+  (void)pkey;
+  (void)access_rights;
+  return Error(ENOSYS);
+}
+
+} // namespace pkey_common
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
diff --git a/libc/src/sys/mman/linux/pkey_alloc.cpp b/libc/src/sys/mman/linux/pkey_alloc.cpp
new file mode 100644
index 0000000000000..baf32013bc5c7
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_alloc.cpp
@@ -0,0 +1,37 @@
+//===---------- Linux implementation of the Linux pkey_alloc function -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/mman/pkey_alloc.h"
+
+#include "hdr/errno_macros.h"             // For ENOSYS
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_alloc,
+                   (unsigned int flags, unsigned int access_rights)) {
+#if !defined(SYS_pkey_alloc)
+  libc_errno = ENOSYS;
+  return -1;
+#else
+  int ret =
+      LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_alloc, flags, access_rights);
+  if (ret < 0) {
+    libc_errno = static_cast<int>(-ret);
+    return -1;
+  }
+  return static_cast<int>(ret);
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_free.cpp b/libc/src/sys/mman/linux/pkey_free.cpp
new file mode 100644
index 0000000000000..0228971bd10f6
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_free.cpp
@@ -0,0 +1,35 @@
+//===---------- Linux implementation of the Linux pkey_free function ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/mman/pkey_free.h"
+
+#include "hdr/errno_macros.h"             // For ENOSYS
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_free, (int pkey)) {
+#if !defined(SYS_pkey_free)
+  libc_errno = ENOSYS;
+  return -1;
+#else
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_free, pkey);
+  if (ret < 0) {
+    libc_errno = static_cast<int>(-ret);
+    return -1;
+  }
+  return 0;
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_get.cpp b/libc/src/sys/mman/linux/pkey_get.cpp
new file mode 100644
index 0000000000000..623b7930c7a23
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_get.cpp
@@ -0,0 +1,35 @@
+//===---------- Linux implementation of the Linux pkey_mprotect function --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/mman/pkey_get.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/architectures.h"
+
+#if defined(LIBC_TARGET_ARCH_IS_X86_64)
+#include "src/sys/mman/linux/x86_64/pkey_common.h"
+#else
+#include "src/sys/mman/linux/generic/pkey_common.h"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_get, (int pkey)) {
+  ErrorOr<int> ret = LIBC_NAMESPACE::pkey_common::pkey_get(pkey);
+  if (!ret.has_value()) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return ret.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_mprotect.cpp b/libc/src/sys/mman/linux/pkey_mprotect.cpp
new file mode 100644
index 0000000000000..15c5d9db39b33
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_mprotect.cpp
@@ -0,0 +1,45 @@
+//===---------- Linux implementation of the Linux pkey_mprotect function --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/mman/pkey_mprotect.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "hdr/types/size_t.h"
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/sys/mman/mprotect.h"
+
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_mprotect,
+                   (void *addr, size_t len, int prot, int pkey)) {
+  // Fall back to mprotect if pkey is -1
+  // to maintain compatibility with kernel versions that don't support pkey.
+  if (pkey == -1) {
+    return LIBC_NAMESPACE::mprotect(addr, len, prot);
+  }
+
+#if !defined(SYS_pkey_mprotect)
+  libc_errno = ENOSYS;
+  return -1;
+#else
+  int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_mprotect, addr, len,
+                                              prot, pkey);
+  if (ret < 0) {
+    libc_errno = -ret;
+    return -1;
+  }
+  return 0;
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_set.cpp b/libc/src/sys/mman/linux/pkey_set.cpp
new file mode 100644
index 0000000000000..7921443f688d3
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_set.cpp
@@ -0,0 +1,35 @@
+//===---------- Linux implementation of the Linux pkey_mprotect function --===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/sys/mman/pkey_set.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+#if defined(LIBC_TARGET_ARCH_IS_X86_64)
+#include "src/sys/mman/linux/x86_64/pkey_common.h"
+#else
+#include "src/sys/mman/linux/generic/pkey_common.h"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_set, (int pkey, unsigned int access_rights)) {
+  ErrorOr<int> ret = LIBC_NAMESPACE::pkey_common::pkey_set(pkey, access_rights);
+  if (!ret.has_value()) {
+    libc_errno = ret.error();
+    return -1;
+  }
+  return ret.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/x86_64/CMakeLists.txt b/libc/src/sys/mman/linux/x86_64/CMakeLists.txt
new file mode 100644
index 0000000000000..1ce23af6dbd2a
--- /dev/null
+++ b/libc/src/sys/mman/linux/x86_64/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_header_library(
+  pkey_common
+  HDRS
+    pkey_common.h
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.stdint_proxy
+    libc.src.__support.common
+    libc.src.__support.error_or
+)
diff --git a/libc/src/sys/mman/linux/x86_64/pkey_common.h b/libc/src/sys/mman/linux/x86_64/pkey_common.h
new file mode 100644
index 0000000000000..bffa9feaed06c
--- /dev/null
+++ b/libc/src/sys/mman/linux/x86_64/pkey_common.h
@@ -0,0 +1,71 @@
+#ifndef LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
+#define LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "hdr/stdint_proxy.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+
+#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
+#error "Invalid include"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+namespace pkey_common {
+namespace internal {
+
+constexpr int MAX_KEY = 15;
+constexpr int KEY_MASK = 0x3;
+constexpr int BITS_PER_KEY = 2;
+
+// This will SIGILL on CPUs that don't support PKU / OSPKE,
+// but this case should never be reached as a prior pkey_alloc invocation
+// would have failed more gracefully.
+LIBC_INLINE uint32_t read_prku() {
+  uint32_t pkru = 0;
+  uint32_t edx = 0;
+  LIBC_INLINE_ASM("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
+  return pkru;
+}
+
+// This will SIGILL on CPUs that don't support PKU / OSPKE,
+// but this case should never be reached as a prior pkey_alloc invocation
+// would have failed more gracefully.
+LIBC_INLINE void write_prku(uint32_t pkru) {
+  LIBC_INLINE_ASM("wrpkru" : : "a"(pkru), "d"(0), "c"(0));
+}
+
+} // namespace internal
+
+// x86_64 implementation of pkey_get.
+// Returns the access rights for the given pkey on success, errno otherwise.
+LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
+  if (pkey < 0 || pkey > internal::MAX_KEY) {
+    return Error(EINVAL);
+  }
+
+  uint32_t pkru = internal::read_prku();
+  return (pkru >> (pkey * internal::BITS_PER_KEY)) & internal::KEY_MASK;
+}
+
+// x86_64 implementation of pkey_set.
+// Returns 0 on success, errno otherwise.
+LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
+  if (pkey < 0 || pkey > internal::MAX_KEY ||
+      access_rights > internal::KEY_MASK) {
+    return Error(EINVAL);
+  }
+
+  uint32_t pkru = internal::read_prku();
+  pkru &= ~(internal::KEY_MASK << (pkey * internal::BITS_PER_KEY));
+  pkru |=
+      ((access_rights & internal::KEY_MASK) << (pkey * internal::BITS_PER_KEY));
+  internal::write_prku(pkru);
+
+  return 0;
+}
+
+} // namespace pkey_common
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
diff --git a/libc/src/sys/mman/pkey_alloc.h b/libc/src/sys/mman/pkey_alloc.h
new file mode 100644
index 0000000000000..c63c6a36c8021
--- /dev/null
+++ b/libc/src/sys/mman/pkey_alloc.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_alloc function -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_ALLOC_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_ALLOC_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_alloc(unsigned int flags, unsigned int access_rights);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_ALLOC_H
diff --git a/libc/src/sys/mman/pkey_free.h b/libc/src/sys/mman/pkey_free.h
new file mode 100644
index 0000000000000..a357e9b8c847b
--- /dev/null
+++ b/libc/src/sys/mman/pkey_free.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_free function ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_FREE_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_FREE_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_free(int pkey);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_FREE_H
diff --git a/libc/src/sys/mman/pkey_get.h b/libc/src/sys/mman/pkey_get.h
new file mode 100644
index 0000000000000..d41afe08ae371
--- /dev/null
+++ b/libc/src/sys/mman/pkey_get.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_get function -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_GET_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_GET_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_get(int pkey);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_GET_H
diff --git a/libc/src/sys/mman/pkey_mprotect.h b/libc/src/sys/mman/pkey_mprotect.h
new file mode 100644
index 0000000000000..4d19348ef09db
--- /dev/null
+++ b/libc/src/sys/mman/pkey_mprotect.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pkey_mprotect function --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
+
+#include "src/__support/macros/config.h"
+#include "hdr/types/size_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_mprotect(void *addr, size_t len, int prot, int pkey);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
diff --git a/libc/src/sys/mman/pkey_set.h b/libc/src/sys/mman/pkey_set.h
new file mode 100644
index 0000000000000..55bafbd11d709
--- /dev/null
+++ b/libc/src/sys/mman/pkey_set.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_set function -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_SET_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_SET_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_set(int pkey, unsigned int access_rights);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_SET_H
diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt
index a362c1cf61cbc..721f89961f7c0 100644
--- a/libc/test/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/test/src/sys/mman/linux/CMakeLists.txt
@@ -67,6 +67,27 @@ add_libc_unittest(
 )
 
 
+add_libc_unittest(
+  pkey_test
+  SUITE
+    libc_sys_mman_unittests
+  SRCS
+    pkey_test.cpp
+  DEPENDS
+    libc.hdr.errno_macros
+    libc.hdr.signal_macros
+    libc.hdr.types.size_t
+    libc.src.sys.mman.mmap
+    libc.src.sys.mman.munmap
+    libc.src.sys.mman.pkey_alloc
+    libc.src.sys.mman.pkey_free
+    libc.src.sys.mman.pkey_get
+    libc.src.sys.mman.pkey_mprotect
+    libc.src.sys.mman.pkey_set
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
+)
+
 add_libc_unittest(
   posix_madvise_test
   SUITE
diff --git a/libc/test/src/sys/mman/linux/pkey_test.cpp b/libc/test/src/sys/mman/linux/pkey_test.cpp
new file mode 100644
index 0000000000000..9c6feae2d457b
--- /dev/null
+++ b/libc/test/src/sys/mman/linux/pkey_test.cpp
@@ -0,0 +1,241 @@
+//===-- Unit tests for pkey functions -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
...
[truncated]

Copy link

github-actions bot commented Oct 7, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@michaelrj-google michaelrj-google left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks good, but you need to update the headergen file in include/sys/mman.yaml

LIBC_INLINE uint32_t read_prku() {
uint32_t pkru = 0;
uint32_t edx = 0;
LIBC_INLINE_ASM("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we migrating back to the macro approach for ASM?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize there was a preference. Switched to use asm volatile directly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For setjmp/longjmp we used [[gnu::naked]] so that the whole function was assembly. I'd need to dig up the discussion but I'd say either is probably fine in this case.

@jtstogel jtstogel changed the title Implement pkey_alloc/free/get/set/mprotect for x86_64 linux [libc] Implement pkey_alloc/free/get/set/mprotect for x86_64 linux Oct 7, 2025
namespace pkey_common {

LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
(void)pkey;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use [[maybe_unused]] on pkey declaration instead.

return Error(ENOSYS);
}

LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use [[maybe_unused]] instead of (void)

// Fall back to mprotect if pkey is -1
// to maintain compatibility with kernel versions that don't support pkey.
if (pkey == -1) {
return LIBC_NAMESPACE::mprotect(addr, len, prot);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this entrypoint dependency be broken up?

LIBC_INLINE uint32_t read_prku() {
uint32_t pkru = 0;
uint32_t edx = 0;
asm volatile("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// but this case should never be reached as a prior pkey_alloc invocation
// would have failed more gracefully.
LIBC_INLINE void write_prku(uint32_t pkru) {
asm volatile("wrpkru" : : "a"(pkru), "d"(0), "c"(0));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

uint32_t pkru = internal::read_prku();
return (pkru >> (pkey * internal::BITS_PER_KEY)) & internal::KEY_MASK;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to overflow on this shift?

Comment on lines +68 to +70
pkru &= ~(internal::KEY_MASK << (pkey * internal::BITS_PER_KEY));
pkru |=
((access_rights & internal::KEY_MASK) << (pkey * internal::BITS_PER_KEY));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar question about potential overflow here.

Comment on lines +87 to +88
libc.test.UnitTest.ErrnoCheckingTest
libc.test.UnitTest.ErrnoSetterMatcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need these any more, right @vonosmas ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do? At least I didn't remember making any changes that would make these dependencies added automatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bazel "Peripheral" support tier build system: utils/bazel libc
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants